package com.uidp.security;

import java.util.Set;
import java.util.TreeSet;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.servlet.http.HttpServletRequest;
import com.acsior.sbc.core.IMessageKey;
import com.acsior.wu.exception.WebUtilsException;
import com.acsior.wu.messanger.Messenger;
import com.acsior.wu.request.RequestManager;
import com.uidp.domain.Users;

/**
 * Контрол на достъпа на потребителите до страниците. Базок клас използван катo
 * услуга от наследниците си.
 * 
 */
class AccessFilter {

	/** Нещо за да се оправим с пренасочванията */
	class Redirect {

		private IMessageKey msg;

		private String outcome;

		private String param;

		/**
		 * @return стойността на msg
		 */
		IMessageKey getMsg() {
			return this.msg;
		}

		/**
		 * @return стойността на outcome
		 */
		String getOutcome() {
			return this.outcome;
		}

		/**
		 * @return стойността на param
		 */
		String getParam() {
			return this.param;
		}

		/** @return да ако проверките са минали и не трябва да се предприема нищо */
		boolean isOK() {
			return this.msg == null && this.outcome == null;
		}

		/**
		 * Ако индекса е неотрицателен, то се навигира към страницата със
		 * съответния индекс. Ако няма такъв индекс, на response-a се слага
		 * status 500 (Internal Error).
		 * 
		 * @param facesContext
		 *            текущия <code>FacesContext</code>
		 */
		void redirect(FacesContext facesContext) {
			if (getOutcome() != null) {
				if (getOutcome().equals(HOME)) {
					try {
						RequestManager.navigate(indexWOExt, null);
					} catch (WebUtilsException e) {
						Messenger.addException(e);
					}
				}

				if (getMsg() != null) {
					if (getParam() != null) {
						Messenger.addErrorMessage(getMsg(), getParam());
					} else {
						Messenger.addErrorMessage(getMsg());
					}
				}
				facesContext.getApplication().getNavigationHandler()
						.handleNavigation(facesContext, null, getOutcome());
			}
		}

		/**
		 * @param msg
		 *            се залага в msg
		 */
		void setMsg(IMessageKey msg) {
			this.msg = msg;
		}

		/**
		 * @param outcome
		 *            се залага в outcome
		 */
		void setOutcome(String outcome) {
			this.outcome = outcome;
		}

		/**
		 * @param param
		 *            се залага в param
		 */
		void setParam(String param) {
			this.param = param;
		}
	}

	/** Суфикси за които задължително се налага филтрация на ресурси */
	private static Set<String> FILTER_SUFFIX_SET = new TreeSet<String>();

	private static final String HOME = "home";

	/** Index страница в приложението без разширение. */
	private static final String indexWOExt = "/pages/common/home";

	/** Outcome към login. */
	private static final String LOGIN_OUTCOME = "login";

	/** Login страница в приложението без разширение. */
	private static final String loginWOExt = "/login";

	/** Префикси на филтрирани ресурси, които се пропускат */
	private static Set<String> SKIP_PREFIX_SET = new TreeSet<String>();

	/** Наклонена черта. */
	private static final String SLASH = "/";

	static {
		FILTER_SUFFIX_SET.add(".jsp");
		FILTER_SUFFIX_SET.add(".jsf");
		FILTER_SUFFIX_SET.add(".faces");
		FILTER_SUFFIX_SET.add(".xhtml");

		SKIP_PREFIX_SET.add("/a4j");
	}

	/**
	 * Проверява дали има текущ потребител. Ако потребителя не е логнат, то се
	 * препраща към login формата. Ако е логнат но е с изтекла сесия се препраща
	 * към страница с пояснение че сесията му е изтекла. Ако потребителят е
	 * валиден но страницата не е валидна - той се изпраща към index страницата.
	 * Създава обект в който се крият данните за пренасочванията.
	 * 
	 * @param arg0
	 *            събитие по faces-а
	 * @return обект в който са заложени съобщения за грешка и навигации
	 */
	Redirect check(PhaseEvent arg0) {

		Redirect res = new Redirect();
		String viewID = getViewID(arg0);

		// ако не се филтрира, не правим нищо
		if (!isFiltered(viewID)) {
			return res;
		}

		viewID = wipeSuffix(viewID);

		// провeрка за автентикация
		SecurityUserManager sum = SecurityUserManager.getInstance();
		Users user = sum.getCurrentUser();
		boolean isLoginPage = viewID.equals(loginWOExt);

		if (user == null && !isLoginPage) {
			res.setOutcome(LOGIN_OUTCOME);
			//res.setMsg(AuditKeys.ILLEGAL_ACCESS);
			return res;
		}

		// ако не е в страницата за вход и няма достъп до текущата страница
		if (user != null && !isLoginPage && !sum.hasAccessToPage(viewID)) {
			res.setOutcome(HOME);
			//res.setMsg(AuditKeys.ILLEGAL_PAGE_ACCESS);
		}

		return res;
	}

	/**
	 * Изчислява viewID-то. Ако ViewRoot е <code>null</code>, то използва
	 * Request URL. Ако не е, взима неговата стойност. Суфикса не се маха.
	 * 
	 * @param arg0
	 * @return оформения ключ, така както е във faces_config
	 */
	String getViewID(PhaseEvent arg0) {
		FacesContext facesContext = arg0.getFacesContext();
		if (facesContext.getViewRoot() == null) {
			ExternalContext extContext = facesContext.getExternalContext();
			String requestURI = ((HttpServletRequest) extContext.getRequest()).getRequestURI();

			return requestURI.substring(requestURI.indexOf(SLASH,
					requestURI.indexOf(SLASH) + SLASH.length()));
		}

		return facesContext.getViewRoot().getViewId();
	}

	/**
	 * Проверява дали поисканото URL трабва да бъде проверено за наличие на
	 * права.
	 * 
	 * @param suffixedViewID
	 * @return да, ако трябва да се стартира процедура по филтрация
	 */
	boolean isFiltered(String suffixedViewID) {
		for (String s : FILTER_SUFFIX_SET) {
			if (suffixedViewID.endsWith(s)) {
				for (String ss : SKIP_PREFIX_SET) {
					if (suffixedViewID.startsWith(ss)) {
						return false;
					}
				}

				return true;
			}
		}

		return false;
	}

	/**
	 * Премахва суфикса от кода, и го прави подходящ за проверка.
	 * 
	 * @param viewID
	 *            достъпвана страница със суфикс
	 * @return нещото без суфикс разбира се
	 */
	String wipeSuffix(String viewID) {
		return viewID.substring(0, viewID.lastIndexOf("."));
	}
}
